{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import codecs\n",
    "import numpy as np\n",
    "import copy\n",
    "import time\n",
    "import random"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "entities2id = {}\n",
    "relations2id = {}\n",
    "\n",
    "id2entity = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "LOSS_LS = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "word2vec = {}\n",
    "pre_path = '../../TCM embedding/result/TCM_w2v.txt'\n",
    "    \n",
    "with codecs.open(pre_path, 'r', 'utf-8') as input_data:\n",
    "    for line in input_data.readlines():\n",
    "        word2vec[line.split()[0]] = [float(i) for i in list(line.split()[1:])]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def dataloader(file1, file2, file3):\n",
    "    print(\"load file...\")\n",
    "\n",
    "    entity = []\n",
    "    relation = []\n",
    "    \n",
    "    with open(file2, 'r',encoding=\"utf-8\") as f1, open(file3, 'r',encoding=\"utf-8\") as f2:\n",
    "        lines1 = f1.readlines()\n",
    "        lines2 = f2.readlines()\n",
    "        for line in lines1:\n",
    "            line = line.strip().split('\\t')\n",
    "            if len(line) != 2:\n",
    "                continue\n",
    "            entities2id[line[0]] = int(line[1])\n",
    "            entity.append(line[1])\n",
    "\n",
    "        for line in lines2:\n",
    "            line = line.strip().split('\\t')\n",
    "            if len(line) != 2:\n",
    "                continue\n",
    "            relations2id[line[0]] = int(line[1])\n",
    "            relation.append(line[1])\n",
    "\n",
    "\n",
    "    triple_list = []\n",
    "\n",
    "    with codecs.open(file1, 'r',encoding = \"utf-8\") as f:\n",
    "        content = f.readlines()\n",
    "        for line in content:\n",
    "            triple = line.strip().split(\"\\t\")\n",
    "            if len(triple) != 3:\n",
    "                continue\n",
    "            h_ = entities2id[triple[0]]\n",
    "            r_ = relations2id[triple[1]]\n",
    "            t_ = entities2id[triple[2]]\n",
    "\n",
    "\n",
    "            triple_list.append([h_, r_, t_])\n",
    "\n",
    "    print(\"Complete load. entity : %d , relation : %d , triple : %d\" % (\n",
    "    len(entity), len(relation), len(triple_list)))\n",
    "    id2entity = {v:k for k,v in entities2id.items()}\n",
    "    return entity, relation, triple_list,id2entity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def norm_l1(h, r, t):\n",
    "    return np.sum(np.fabs(h + r - t))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def norm_l2(h, r, t):\n",
    "    return np.sum(np.square(h + r - t))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TransE:\n",
    "    def __init__(self, entity, relation, triple_list, embedding_dim=200, lr=0.01, margin=1.0, norm=1):\n",
    "        self.entities = entity\n",
    "        self.relations = relation\n",
    "        self.triples = triple_list\n",
    "        self.dimension = embedding_dim\n",
    "        self.learning_rate = lr\n",
    "        self.margin = margin\n",
    "        self.norm = norm\n",
    "        self.loss = 0.0\n",
    "\n",
    "    def data_initialise(self):\n",
    "        entityVectorList = {}\n",
    "        relationVectorList = {}\n",
    "        for entity in self.entities:\n",
    "            ##print(\"entity\",entity)##\n",
    "            #entity_vector = np.random.uniform(-6.0 / np.sqrt(self.dimension), 6.0 / np.sqrt(self.dimension),\n",
    "            #                                  self.dimension)\n",
    "            word_entity = id2entity[int(entity)]\n",
    "            entity_vector = np.zeros(self.dimension)\n",
    "            for char in word_entity:\n",
    "                try:\n",
    "                    entity_vector += word2vec[char]\n",
    "                except:\n",
    "                    entity_vector += np.random.uniform(-6.0 / np.sqrt(self.dimension), 6.0 / np.sqrt(self.dimension),\n",
    "                                              self.dimension)\n",
    "            entityVectorList[int(entity)] = np.asarray(entity_vector)\n",
    "\n",
    "        for relation in self.relations:\n",
    "            ##print(\"entity\",relation)##\n",
    "            relation_vector = np.random.uniform(-6.0 / np.sqrt(self.dimension), 6.0 / np.sqrt(self.dimension),\n",
    "                                                self.dimension)\n",
    "            \n",
    "            relation_vector = self.normalization(relation_vector)\n",
    "            relationVectorList[int(relation)] = relation_vector\n",
    "\n",
    "        self.entities = entityVectorList\n",
    "        self.relations = relationVectorList\n",
    "        print(\"entityVectorList:\",entityVectorList[0].shape)\n",
    "        print(\"relationVectorList:\",relationVectorList[0].shape)\n",
    "        \n",
    "    def normalization(self, vector):\n",
    "        return vector / np.linalg.norm(vector)\n",
    "\n",
    "    def training_run(self, epochs=30, nbatches=100, out_file_title = ''):\n",
    "\n",
    "        batch_size = int(len(self.triples) / nbatches)\n",
    "        print(\"batch size: \", batch_size)\n",
    "        for epoch in range(epochs):\n",
    "            start = time.time()\n",
    "            self.loss = 0.0\n",
    "            # Normalise the embedding of the entities to 1\n",
    "            for entity in self.entities.keys():\n",
    "                self.entities[entity] = self.normalization(self.entities[entity]);\n",
    "\n",
    "            for batch in range(nbatches):\n",
    "                batch_samples = random.sample(self.triples, batch_size)\n",
    "\n",
    "                Tbatch = []\n",
    "                for sample in batch_samples:\n",
    "                    corrupted_sample = copy.deepcopy(sample)\n",
    "                    pr = np.random.random(1)[0]\n",
    "                    if pr > 0.5:\n",
    "                        # change the head entity\n",
    "                        corrupted_sample[0] = random.sample(self.entities.keys(), 1)[0]\n",
    "                        while corrupted_sample[0] == sample[0]:\n",
    "                            corrupted_sample[0] = random.sample(self.entities.keys(), 1)[0]\n",
    "                    else:\n",
    "                        # change the tail entity\n",
    "                        corrupted_sample[2] = random.sample(self.entities.keys(), 1)[0]\n",
    "                        while corrupted_sample[2] == sample[2]:\n",
    "                            corrupted_sample[2] = random.sample(self.entities.keys(), 1)[0]\n",
    "                    if (sample, corrupted_sample) not in Tbatch:\n",
    "                        Tbatch.append((sample, corrupted_sample))\n",
    "\n",
    "                self.update_triple_embedding(Tbatch)\n",
    "            end = time.time()\n",
    "            print(\"epoch: \", epoch, \"cost time: %s\" % (round((end - start), 3)))\n",
    "            print(\"running loss: \", self.loss)\n",
    "            LOSS_LS.append(self.loss)\n",
    "\n",
    "        with codecs.open(out_file_title +\"TransE_entity_\" + str(self.dimension) + \"dim_batch\" + str(batch_size), \"w\") as f1:\n",
    "\n",
    "            for e in self.entities.keys():\n",
    "                # f1.write(\"\\t\")\n",
    "                # f1.write(e + \"\\t\")\n",
    "                f1.write(str(list(self.entities[e])))\n",
    "                f1.write(\"\\n\")\n",
    "\n",
    "        with codecs.open(out_file_title +\"TransE_relation_\" + str(self.dimension) + \"dim_batch\" + str(batch_size), \"w\") as f2:\n",
    "            for r in self.relations.keys():\n",
    "                # f2.write(\"\\t\")\n",
    "                # f2.write(r + \"\\t\")\n",
    "                f2.write(str(list(self.relations[r])))\n",
    "                f2.write(\"\\n\")\n",
    "\n",
    "    def update_triple_embedding(self, Tbatch):\n",
    "        # deepcopy 可以保证，即使list嵌套list也能让各层的地址不同， 即这里copy_entity 和\n",
    "        # entitles中所有的elements都不同\n",
    "        copy_entity = copy.deepcopy(self.entities)\n",
    "        copy_relation = copy.deepcopy(self.relations)\n",
    "\n",
    "        for correct_sample, corrupted_sample in Tbatch:\n",
    "            correct_copy_head = copy_entity[correct_sample[0]]\n",
    "            correct_copy_tail = copy_entity[correct_sample[2]]\n",
    "            relation_copy = copy_relation[correct_sample[1]]\n",
    "\n",
    "            corrupted_copy_head = copy_entity[corrupted_sample[0]]\n",
    "            corrupted_copy_tail = copy_entity[corrupted_sample[2]]\n",
    "\n",
    "            correct_head = self.entities[correct_sample[0]]\n",
    "            correct_tail = self.entities[correct_sample[2]]\n",
    "            relation = self.relations[correct_sample[1]]\n",
    "\n",
    "            corrupted_head = self.entities[corrupted_sample[0]]\n",
    "            corrupted_tail = self.entities[corrupted_sample[2]]\n",
    "\n",
    "            # calculate the distance of the triples\n",
    "            if self.norm == 1:\n",
    "                correct_distance = norm_l1(correct_head, relation, correct_tail)\n",
    "                corrupted_distance = norm_l1(corrupted_head, relation, corrupted_tail)\n",
    "\n",
    "            else:\n",
    "                correct_distance = norm_l2(correct_head, relation, correct_tail)\n",
    "                corrupted_distance = norm_l2(corrupted_head, relation, corrupted_tail)\n",
    "\n",
    "            loss = self.margin + correct_distance - corrupted_distance\n",
    "\n",
    "            if loss > 0:\n",
    "                self.loss += loss\n",
    "                #print(\"loss:\",loss)\n",
    "                correct_gradient = 2 * (correct_head + relation - correct_tail)\n",
    "                corrupted_gradient = 2 * (corrupted_head + relation - corrupted_tail)\n",
    "\n",
    "                if self.norm == 1:\n",
    "                    for i in range(len(correct_gradient)):\n",
    "                        if correct_gradient[i] > 0:\n",
    "                            correct_gradient[i] = 1\n",
    "                        else:\n",
    "                            correct_gradient[i] = -1\n",
    "\n",
    "                        if corrupted_gradient[i] > 0:\n",
    "                            corrupted_gradient[i] = 1\n",
    "                        else:\n",
    "                            corrupted_gradient[i] = -1\n",
    "\n",
    "                correct_copy_head -= self.learning_rate * correct_gradient\n",
    "                relation_copy -= self.learning_rate * correct_gradient\n",
    "                correct_copy_tail -= -1 * self.learning_rate * correct_gradient\n",
    "\n",
    "                relation_copy -= -1 * self.learning_rate * corrupted_gradient\n",
    "                if correct_sample[0] == corrupted_sample[0]:\n",
    "                    # if corrupted_triples replaces the tail entity, the head entity's embedding need to be updated twice\n",
    "                    correct_copy_head -= -1 * self.learning_rate * corrupted_gradient\n",
    "                    corrupted_copy_tail -= self.learning_rate * corrupted_gradient\n",
    "                elif correct_sample[2] == corrupted_sample[2]:\n",
    "                    # if corrupted_triples replaces the head entity, the tail entity's embedding need to be updated twice\n",
    "                    corrupted_copy_head -= -1 * self.learning_rate * corrupted_gradient\n",
    "                    correct_copy_tail -= self.learning_rate * corrupted_gradient\n",
    "\n",
    "                # normalising these new embedding vector, instead of normalising all the embedding together\n",
    "                copy_entity[correct_sample[0]] = self.normalization(correct_copy_head)\n",
    "                copy_entity[correct_sample[2]] = self.normalization(correct_copy_tail)\n",
    "                if correct_sample[0] == corrupted_sample[0]:\n",
    "                    # if corrupted_triples replace the tail entity, update the tail entity's embedding\n",
    "                    copy_entity[corrupted_sample[2]] = self.normalization(corrupted_copy_tail)\n",
    "                elif correct_sample[2] == corrupted_sample[2]:\n",
    "                    # if corrupted_triples replace the head entity, update the head entity's embedding\n",
    "                    copy_entity[corrupted_sample[0]] = self.normalization(corrupted_copy_head)\n",
    "                # the paper mention that the relation's embedding don't need to be normalised\n",
    "                copy_relation[correct_sample[1]] = relation_copy\n",
    "                # copy_relation[correct_sample[1]] = self.normalization(relation_copy)\n",
    "\n",
    "        self.entities = copy_entity\n",
    "        self.relations = copy_relation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load file...\n",
      "Complete load. entity : 463 , relation : 5 , triple : 1573\n",
      "entityVectorList: (200,)\n",
      "relationVectorList: (200,)\n",
      "batch size:  15\n",
      "epoch:  0 cost time: 0.244\n",
      "running loss:  811.6605495113873\n",
      "epoch:  1 cost time: 0.235\n",
      "running loss:  496.2576237111866\n",
      "epoch:  2 cost time: 0.254\n",
      "running loss:  391.5193399615886\n",
      "epoch:  3 cost time: 0.239\n",
      "running loss:  334.438812168076\n",
      "epoch:  4 cost time: 0.295\n",
      "running loss:  328.6321662235757\n",
      "epoch:  5 cost time: 0.235\n",
      "running loss:  298.31971473384374\n",
      "epoch:  6 cost time: 0.231\n",
      "running loss:  267.91664440971294\n",
      "epoch:  7 cost time: 0.273\n",
      "running loss:  281.53117294194834\n",
      "epoch:  8 cost time: 0.19\n",
      "running loss:  255.06154891631388\n",
      "epoch:  9 cost time: 0.222\n",
      "running loss:  269.9876303391885\n",
      "epoch:  10 cost time: 0.229\n",
      "running loss:  262.0020055491269\n",
      "epoch:  11 cost time: 0.193\n",
      "running loss:  213.62501664641803\n",
      "epoch:  12 cost time: 0.25\n",
      "running loss:  212.2932419591822\n",
      "epoch:  13 cost time: 0.199\n",
      "running loss:  205.23733223915764\n",
      "epoch:  14 cost time: 0.226\n",
      "running loss:  220.24243519117027\n",
      "epoch:  15 cost time: 0.269\n",
      "running loss:  215.90378878174488\n",
      "epoch:  16 cost time: 0.198\n",
      "running loss:  199.2514666331424\n",
      "epoch:  17 cost time: 0.255\n",
      "running loss:  174.55659173147828\n",
      "epoch:  18 cost time: 0.277\n",
      "running loss:  195.83349654370284\n",
      "epoch:  19 cost time: 0.28\n",
      "running loss:  198.99777819152737\n",
      "epoch:  20 cost time: 0.2\n",
      "running loss:  190.55401233142553\n",
      "epoch:  21 cost time: 0.259\n",
      "running loss:  176.24154475874363\n",
      "epoch:  22 cost time: 0.304\n",
      "running loss:  163.47206178970922\n",
      "epoch:  23 cost time: 0.257\n",
      "running loss:  146.44520965168908\n",
      "epoch:  24 cost time: 0.191\n",
      "running loss:  158.15124467760947\n",
      "epoch:  25 cost time: 0.197\n",
      "running loss:  137.6502875390069\n",
      "epoch:  26 cost time: 0.175\n",
      "running loss:  128.91297546311847\n",
      "epoch:  27 cost time: 0.189\n",
      "running loss:  134.28032932614693\n",
      "epoch:  28 cost time: 0.285\n",
      "running loss:  139.18189084009674\n",
      "epoch:  29 cost time: 0.179\n",
      "running loss:  142.5856890574975\n"
     ]
    }
   ],
   "source": [
    "location = './'\n",
    "file1 = \"../data/triple_KE-simplified.txt\"\n",
    "file2 = \"../data/entity2id.txt\"\n",
    "file3 = \"../data/relation2id.txt\"\n",
    "\n",
    "entity_set, relation_set, triple_list , id2entity = dataloader(file1, file2, file3)\n",
    "    \n",
    "# modify by yourself\n",
    "transE = TransE(entity_set, relation_set, triple_list, embedding_dim=200, lr=0.01, margin=1.0, norm=2)\n",
    "transE.data_initialise()\n",
    "transE.training_run(out_file_title=\"qianjinfang_\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[811.6605495113873,\n",
       " 496.2576237111866,\n",
       " 391.5193399615886,\n",
       " 334.438812168076,\n",
       " 328.6321662235757,\n",
       " 298.31971473384374,\n",
       " 267.91664440971294,\n",
       " 281.53117294194834,\n",
       " 255.06154891631388,\n",
       " 269.9876303391885,\n",
       " 262.0020055491269,\n",
       " 213.62501664641803,\n",
       " 212.2932419591822,\n",
       " 205.23733223915764,\n",
       " 220.24243519117027,\n",
       " 215.90378878174488,\n",
       " 199.2514666331424,\n",
       " 174.55659173147828,\n",
       " 195.83349654370284,\n",
       " 198.99777819152737,\n",
       " 190.55401233142553,\n",
       " 176.24154475874363,\n",
       " 163.47206178970922,\n",
       " 146.44520965168908,\n",
       " 158.15124467760947,\n",
       " 137.6502875390069,\n",
       " 128.91297546311847,\n",
       " 134.28032932614693,\n",
       " 139.18189084009674,\n",
       " 142.5856890574975]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "LOSS_LS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['font.sans-serif']=['simsun'] #用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus']=False #用来正常显示负号"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()#创建一个figure \n",
    "ax.plot(LOSS_LS, '-r',label='loss')\n",
    "####打开网格\n",
    "ax.grid(True)\n",
    "\n",
    "####定义x, y轴的名称\n",
    "ax.set_xlabel('epoch',fontsize=15)\n",
    "ax.set_ylabel('loss',fontsize=15)\n",
    "\n",
    "####定义标题\n",
    "fig.suptitle('TransE模型损失函数变化曲线',fontsize=15)\n",
    "\n",
    "####展示图例 legend loc=是用来定义图例的位置的，还有很多选择，大家可以自己尝试\n",
    "ax.legend(loc = 'upper right')\n",
    "\n",
    "plt.savefig(\"TransE模型损失函数变化曲线\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.6.13 64-bit ('pytorch-cpu': conda)",
   "language": "python",
   "name": "python361364bitpytorchcpucondad83331f650914d99bc445256c580cc5c"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
